package io.sdph.core.database;

import io.sdph.core.database.exceptions.DatabaseException;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import org.apache.log4j.Logger;

public abstract class Transaction {
	
	private static final Logger log = Logger.getLogger(Transaction.class);
	
	private Connection con;
	private Object result;
	
	public Transaction() throws DatabaseException {
		DatabaseUtils utils = DatabaseUtils.getInstance();
		con = utils.getConnection();
		try {
			result = doIt();
		} catch (Throwable e) {
			try {
				log.error("Rolling back transaction.");
				con.rollback();
			} catch (SQLException e1) {
				log.error("Cannot roll back transaction.");
			}
			throw new DatabaseException(e);
		} finally {
			utils.closeConnection(con);
		}
	}
	public abstract Object doIt() throws DatabaseException, SQLException;
	
	public ResultSet select(String query, Object... params) throws DatabaseException {
		ResultSet rs = null;

		PreparedStatement ps = null;
		try {
			ps = con.prepareStatement(query);
			int i = 1;
			for (Object param : params) {
				ps.setObject(i++, param);
			}
			rs = ps.executeQuery();
		} catch (SQLException e) {
			StringBuilder error = new StringBuilder("SQL Error:\n");
			error.append(query).append("\n with params:");
			int i = 1;
			for (Object param : params) {
				error.append("\n [").append(i).append("] : ")
						.append(param.toString());
			}
			throw new DatabaseException(error.toString(), e);
		}
		return rs;
	}
	
	private int update(String query, Integer statement, Object... params) throws DatabaseException {
		int l = 0;
		PreparedStatement ps = null;
		try {
			ps = con.prepareStatement(query);
			int i = 1;
			for (Object param : params) {
				ps.setObject(i++, param);
			}
			if ( statement != null ) {
				l = ps.executeUpdate(query, statement);
			} else {
				l = ps.executeUpdate(query);
			}
		} catch (SQLException e) {
			StringBuilder error = new StringBuilder("SQL Error:\n");
			error.append(query).append("\n with params:");
			int i = 1;
			for (Object param : params) {
				error.append("\n [").append(i).append("] : ")
						.append(param.toString());
			}
			throw new DatabaseException(error.toString(), e);
		}
		return l;
	}
	
	/**
	 * Zwraca ilość zedytowanych wierszy.
	 * 
	 * @param query - kwerenda SQL
	 * @param params - parametry
	 * @return
	 * @throws DatabaseException
	 */
	public int update(String query, Object...params) throws DatabaseException {
		return update(query, null, params);
	}
	
	/**
	 * Zwraca ilość usuniętych wierszy.
	 * 
	 * @param query - kwerenda SQL
	 * @param params - parametry
	 * @return
	 * @throws DatabaseException
	 */
	public int delete(String query, Object... params) throws DatabaseException {
		return update(query, params);
	}
	
	/**
	 * Zwraca id dodanego wieszu.
	 * 
	 * @param query - kwerenda SQL
	 * @param params - parametry
	 * @return
	 * @throws DatabaseException
	 */
	public int insert(String query, Object... params) throws DatabaseException {
		return update(query, Statement.RETURN_GENERATED_KEYS, params);
	}
	
	public Object getResult() {
		return this.result;
	}
}
